﻿using System;
using System.IO;
using System.Reflection;
using System.ServiceProcess;
using System.Text;
using System.Configuration.Install;
using System.Collections;
using System.Linq;

using log4net;
using NHibernate.Tool.hbm2ddl;


namespace Pink.Scheduler.Service
{
	static class Program
	{
		private static string log4netConfig = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, 
			"log4net.config");
		private static ILog log = LogManager.GetLogger("SchedulerStarter");
		private static SchedulerService service;
		private static string nHibernateConfig;

		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		[LoaderOptimization(LoaderOptimization.MultiDomainHost)]
		static int Main(string[] args)
		{
			string oldCurrentDirectory = Directory.GetCurrentDirectory();
			// пути log4net -- относительно текущей директории
			// а сервисы запускаются с текущей папкой в System32
			Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
			log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo(log4netConfig));
			nHibernateConfig = System.Configuration.ConfigurationSettings.AppSettings["NHibernateConfigPath"]
				?? "nhibernate.config";
			// запуск без аргументов -- это или сервис или вывод в консоль, как пользоваться
			if (args.Length == 0)
			{
				if (Environment.UserInteractive)
				{
					string desc = ((AssemblyDescriptionAttribute)Attribute.GetCustomAttribute(Assembly.GetExecutingAssembly(),
						typeof(AssemblyDescriptionAttribute))).Description;
					PrintUsage(desc);
					return 1;
				}
				else
				{
					// System.Diagnostics.Debugger.Break();
					ServiceBase.Run(new SchedulerService(nHibernateConfig, null));
					return 0;
				}
			}
			// все дальнейшие команды требуют файла настроек
			if (!File.Exists(nHibernateConfig))
			{
				Console.WriteLine("Не найден файл настроек NHibernate {0}.", nHibernateConfig);
				return 1;
			}
			// запуск через консоль
			try
			{
				switch (args[0])
				{
					case "run":
						try
						{
							// подпишемся на события ctrl+c, ctrl+break
							ConsoleCtrl cc = new ConsoleCtrl();
							cc.ControlEvent += new ConsoleCtrl.ControlEventHandler(cc_ControlEvent);
							// сохраним ссылку, чтобы потом остановить
							string[] importOverrides = args.Length > 1 ? args.Skip(1).ToArray() : null;
							service = new SchedulerService(nHibernateConfig, importOverrides);
							service.StartAndSleep(args);
						}
						catch (Exception ex)
						{
							ex.ToString();
							throw;
						}
						return 0;
					case "export":
						if (args.Length < 2)
						{
							PrintUsage("Не указан файл для экспорта.");
							return 1;
						}
						string file = args[1];
						Console.Write("Экспорт схемы в файл {0}...  ", file);
						SchemaExport se = new SchemaExport(GetConfig(nHibernateConfig));
						se.SetOutputFile(file);
						se.Execute(false, false, false);
						Console.WriteLine("Завершён.");
						return 0;
					case "initiate":
						Console.Write("Создание базы данных...  ");
						SchemaExport se2 = new SchemaExport(GetConfig(nHibernateConfig));
						se2.Execute(false, true, false);
						Console.WriteLine("Завершено.");
						return 0;
					case "install":
						string stateFile = Path.ChangeExtension(
							Assembly.GetExecutingAssembly().Location, "InstallState");
						if (File.Exists(stateFile))
						{
							Console.WriteLine("Найден файл {0}! Удалите сервис, вызвав программу с ключом uninstall.",
								Path.GetFileName(stateFile));
							return 1;
						}
						Console.WriteLine("Установка сервиса...  ");
						Install(args);
						Console.WriteLine("Завершена.");
						return 0;
					case "uninstall":
						Console.WriteLine("Удаление сервиса...  ");
						Uninstall(args);
						Console.WriteLine("Завершено.");
						return 0;
					case "validate":
						Console.WriteLine("Проверка схемы БД...");
						SchemaValidator v = new SchemaValidator(GetConfig(nHibernateConfig));
						v.Validate();
						Console.WriteLine("Завершена.");
						return 0;
					default:
						PrintUsage("Неизвестная команда.");
						return 1;
				}
			}
			catch (Exception e)
			{
				Console.Error.WriteLine(e);
				return 2;
			}
		}

		private static void cc_ControlEvent(ConsoleCtrl.ConsoleEvent consoleEvent)
		{
			Console.WriteLine(">>>> Событие {0}. Остановка сервиса.", consoleEvent);
			service.StopEvent.Set();
		}

		private static void PrintUsage(string firstLine)
		{
			Assembly ass = Assembly.GetExecutingAssembly();
			Console.WriteLine(Resources.Help,
				firstLine,
				Path.GetFileName(ass.Location),
				((AssemblyCopyrightAttribute)Attribute.GetCustomAttribute(ass, typeof(AssemblyCopyrightAttribute))).Copyright);
		}

		/// <summary>
		/// Создать утилиту для экспорта БД
		/// </summary>
		/// <param name="config">Путь к настройкам NHibernate</param>
		/// <returns></returns>
		private static NHibernate.Cfg.Configuration GetConfig(string config)
		{
			NHibernate.Cfg.Configuration cfg = new NHibernate.Cfg.Configuration()
				.Configure(config);
			return cfg;
		}

		/// <summary>
		/// Установить сервис (для запуска из консоли)
		/// </summary>
		private static void Install(string[] args)
		{
			using (AssemblyInstaller inst = new AssemblyInstaller(typeof(Program).Assembly, args))
			{
				IDictionary state = new Hashtable();
				try
				{
					inst.UseNewContext = true;
					inst.Install(state);
					inst.Commit(state);
				}
				catch
				{
					try
					{
						inst.Rollback(state);
					}
					catch (Exception ex)
					{
						// по поводу внешнего не паримся -- его схватит дефолтовый обработчик
						Console.WriteLine("Ошибка отката: {0}", ex);
					}
					throw;
				}
			}
		}
		private static void Uninstall(string[] args)
		{
			using (AssemblyInstaller inst = new AssemblyInstaller(typeof(Program).Assembly, args))
			{
				IDictionary state = new Hashtable();
				inst.Uninstall(state);
			}
		}
	}
}